﻿using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using P2P.Main.Enums;
using P2P.Main.Model;
using P2P.Main.Util;
using P2P.Network.ExtensionMethods;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net.Sockets;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace P2P.Main.Session
{
    public class ActiveNodeSessionManager
    {
        private static readonly object _lock = new object();

        private static ActiveNodeSessionManager? instance;

        private ActiveNodeSessionManager()
        {
            InitHeartbeatTask();
            InitPingAckTask();
        }

        public static ActiveNodeSessionManager Instance
        {
            get 
            {
                if (instance == null)
                {
                    lock (_lock)
                    {
                        if (instance == null)
                        {
                            instance = new ActiveNodeSessionManager();
                        }
                    }
                }
                return instance;
            }
        }

        /// <summary>
        /// 需要更新K桶的客户端
        /// </summary>
        public HashSet<string> NeedUpdateKBucketsClients = new HashSet<string>();

        /// <summary>
        /// Ping命令失败的后补节点
        /// </summary>
        public ConcurrentDictionary<int,List<ActiveNode>> AsyncDelayInsertNodes = new ConcurrentDictionary<int, List<ActiveNode>>();

        /// <summary>
        /// Ping命令没有在指定时间内应答的节点
        /// </summary>
        public ConcurrentDictionary<int, List<ActiveNode>> AsyncDelayDeleteNodes = new ConcurrentDictionary<int, List<ActiveNode>>();

        /// <summary>
        /// 所有客户端K桶集合
        /// </summary>
        public List<List<ActiveNode>> ClientKBuckets = new List<List<ActiveNode>>();

        /// <summary>
        /// 所有在线的客户端集合
        /// </summary>
        public ConcurrentDictionary<string, ActiveNode> ActiveNodeDict = new ConcurrentDictionary<string, ActiveNode>();

        public ActiveNode? Find(string id) 
        {
            if (!string.IsNullOrEmpty(id) && ActiveNodeDict.ContainsKey(id))
            {
                return ActiveNodeDict[id];
            }
            else 
            {
                return null;
            }
        }

        public ActiveNode? Flush(string id, NatType natType)
        {
            lock (ActiveNodeDict) 
            {
                ActiveNode? node = null;

                ActiveNodeDict.TryGetValue(id, out node);

                if (node != null) 
                {
                    node.IsActive = true;
                    node.NatType = natType;
                }
                return node;
            }
        }

        public ActiveNode? Flush(string id, string remoteIP,int remotePort)
        {
            lock (ActiveNodeDict)
            {
                ActiveNode node = null;

                ActiveNodeDict.TryGetValue(id, out node);

                if (node != null)
                {
                    node.IsActive = true;
                    node.LastActiveTime = DateTime.Now;
                }
                else 
                {
                    node = new ActiveNode();
                    node.Id = id;
                    node.IP = remoteIP;
                    node.UdpPublicPort = remotePort;
                    node.LastActiveTime = DateTime.Now;
                    node.IsActive = true;
                    ActiveNodeDict.TryAdd(id, node);
                }
                return node;
            }
        }

        public ActiveNode? Register(string id, int tcpPublicActivePort)
        {
            lock (ActiveNodeDict)
            {
                ActiveNode node = null;

                ActiveNodeDict.TryGetValue(id, out node);

                if (node != null)
                {
                    node.TcpPublicActivePorts.Add(new ActiveNode.TcpPublicActivePort { Port = tcpPublicActivePort,RegisterTime = DateTime.Now});
                }
                
                return node;
            }
        }

        public void InitHeartbeatTask() 
        {
            Task.Run(async () =>
            {
                while (true) 
                {
                    ICollection<ActiveNode> nodes = ActiveNodeDict.Values;

                    foreach (ActiveNode node in nodes)
                    {
                        DateTime now = DateTime.Now;

                        if (now.Ticks - node.LastActiveTime.Ticks > 30 * TimeSpan.TicksPerMillisecond)
                        {
                            Logger.WriteInfo($"Client Node at IP: {node.IP},Port: {node.UdpPublicPort},Name: {node.Name} is offline.");
                            nodes.Remove(node);
                        }
                    }

                    await Task.Delay(100);
                }
            });
        }

        public void InitPingAckTask() 
        {
            Task.Run(async () => 
            {
                while (true)
                {
                    lock (AsyncDelayDeleteNodes)
                    {
                        //移除无效节点
                        foreach (var bucketIndex in AsyncDelayDeleteNodes.Keys)
                        {
                            List<ActiveNode> nodes = AsyncDelayDeleteNodes[bucketIndex];

                            lock (ClientKBuckets) 
                            {
                                foreach (var node in nodes)
                                {
                                    for (int j = 0; j < ClientKBuckets.Count; j++)
                                    {
                                        ClientKBuckets[j] = ClientKBuckets[j].FindAll(n => !n.Id.Equals(node.Id));
                                    }
                                }
                                List<string> ids = nodes.Select(n => n.Id).ToList();
                                NeedUpdateKBucketsClients.RemoveWhere(id => ids.Contains(id));
                            }
                        }
                        AsyncDelayDeleteNodes.Clear();


                        lock (ClientKBuckets)
                        {
                            //添加候选节点
                            foreach (var bucketIndex in AsyncDelayInsertNodes.Keys)
                            {
                                List<ActiveNode> nodes = AsyncDelayInsertNodes[bucketIndex];
                                ClientKBuckets[bucketIndex].AddRange(nodes);

                                List<string> ids = nodes.Select(n => n.Id).ToList();
                                foreach (var id in ids) 
                                {
                                    NeedUpdateKBucketsClients.Add(id);
                                }
                                
                            }
                        }
                        AsyncDelayInsertNodes.Clear();
                    }
                    await Task.Delay(1000 * 30);
                }
            });
        }

        /// <summary>
        /// 计算两个节点ID的异或距离
        /// </summary>
        /// <param name="firstNodeId"></param>
        /// <param name="secondNodeId"></param>
        /// <returns></returns>
        public BigInteger CalculateDistance(BigInteger firstNodeId, BigInteger secondNodeId)
        {
            return firstNodeId ^ secondNodeId;
        }

        /// <summary>
        /// 插入节点到对应的k桶
        /// </summary>
        /// <param name="k">每个节点存储的K桶数 </param>
        /// <param name="maxCapacityKBuckets">最多有多少个K桶</param>
        public void InsertNodeIntoKBucket(Socket sock, ActiveNode newNode, int k, int maxCapacityKBuckets)
        {
            lock (ClientKBuckets)
            {
                List<List<ActiveNode>> kBuckets = ClientKBuckets;
                int bucketIndex = 0;
                while (bucketIndex < maxCapacityKBuckets)
                {
                    //计算新节点到当前桶的距离范围
                    int minDistance = 1 << bucketIndex;
                    int maxDistance = (1 << bucketIndex + 1) - 1;

                    BigInteger newNodeId = BigInteger.Parse(newNode.Id);
                    // 如果新节点在当前桶的距离范围内
                    if (CalculateDistance(newNodeId, minDistance) <= maxDistance)
                    {
                        // 获取当前桶
                        List<ActiveNode> bucket = kBuckets[bucketIndex];

                        if (bucket.Count() < k)
                        {
                            bucket.Add(newNode);
                            List<string> ids = bucket.Select(n => n.Id).ToList();
                            foreach (string id in ids)
                            {
                                NeedUpdateKBucketsClients.Add(id);
                            }
                            return;
                        }

                        //桶已满，ping 桶头部节点检查是否存活
                        ActiveNode headNode = bucket.First();

                        JObject data = new JObject();
                        data.Add("Name", "Ping");
                        data.Add("Method", "Ping");
                        data.Add("Version", "1.0");

                        string pingCommand = JsonConvert.SerializeObject(R<JObject>.Ok(data));

                        for (int i = 0; i < 5; ++i)
                        {
                            sock.SendTo(pingCommand, headNode.IP, headNode.UdpPublicPort);
                        }


                        if (!AsyncDelayDeleteNodes.ContainsKey(bucketIndex))
                        {
                            List<ActiveNode> nodes = new List<ActiveNode>();
                            nodes.Add(newNode);
                            AsyncDelayDeleteNodes.TryAdd(bucketIndex, nodes);
                        }
                        else
                        {
                            AsyncDelayDeleteNodes[bucketIndex].Add(newNode);
                        }

                        if (!AsyncDelayInsertNodes.ContainsKey(bucketIndex))
                        {
                            List<ActiveNode> nodes = new List<ActiveNode>();
                            nodes.Add(newNode);
                            AsyncDelayInsertNodes.TryAdd(bucketIndex, nodes);
                        }
                        else
                        {
                            AsyncDelayInsertNodes[bucketIndex].Add(newNode);
                        }
                    }
                    bucketIndex ++;
                }
            }
        }
    }
}
